package com.slipper.system.codeless.service;

import java.util.List;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;

import com.slipper.constants.Constants;
import com.slipper.core.cache.CacheKey;
import com.slipper.core.cache.CacheUtil;
import com.slipper.core.message.MessageUtil;
import com.slipper.core.message.Topic;
import com.slipper.exception.CommonException;
import com.slipper.system.codeless.bo.*;
import com.slipper.system.codeless.dao.DataFolderDao;
import com.slipper.codeless.trigger.CodelessTrigger;
import com.slipper.util.Tool;
import com.slipper.util.UUID;
import com.slipper.system.codeless.dao.DataModelDao;
import com.slipper.core.transaction.ReadTransactional;
import com.slipper.core.transaction.WriteTransactional;

import org.springframework.stereotype.Service;
import jakarta.annotation.Resource;

@Service
@ReadTransactional
public class DataModelService {
    @Resource
    private DataFolderDao dataFolderDao;
    @Resource
    private DataModelDao dataModelDao;
    @Resource
    private ViewService viewService;
    @Resource
    private DataFieldService dataFieldService;
    @Resource
    private DataFieldRelevanceService dataFieldRelevanceService;
    @Resource
    private DataIndexService dataIndexService;
    @Resource
    private DataTableService dataTableService;
    @Resource
    private DataFormService dataFormService;
    @Resource
    private CodelessTrigger codelessTrigger;

    public DataModelBO queryBy(String id) {
        DataModelBO dataModel = CacheUtil.get(CacheKey.DataModel.PREFIX + id, DataModelBO.class);
        if (dataModel == null) {
            dataModel = dataModelDao.queryBy(id);
            if (Constants.DataModel.TYPE_VIEW.equals(dataModel.getType())) {
                dataModel.setSourceName(viewService.queryBy(dataModel.getSourceId()).getName());
            }
            CacheUtil.set(CacheKey.DataModel.PREFIX + dataModel.getId(), dataModel, 60 * 60);
            CacheUtil.set(CacheKey.DataModel.PREFIX + dataModel.getCode(), dataModel, 60 * 60);
        }
        return dataModel;
    }

    public DataModelBO queryByCode(String code) {
        DataModelBO dataModel = CacheUtil.get(CacheKey.DataModel.PREFIX + code, DataModelBO.class);
        if (dataModel == null) {
            dataModel = dataModelDao.queryByCode(code);
            CacheUtil.set(CacheKey.DataModel.PREFIX + dataModel.getId(), dataModel, 60 * 60);
            CacheUtil.set(CacheKey.DataModel.PREFIX + dataModel.getCode(), dataModel, 60 * 60);
        }
        return dataModel;
    }

    public List<DataModelBO> queryList(DataModelBO bo) {
        return dataModelDao.queryList(bo);
    }

    public PageInfo<DataModelBO> queryPage(DataModelBO bo) {
        PageHelper.startPage(bo.getPageNumber(), bo.getPageSize());
        return new PageInfo<>(dataModelDao.queryList(bo));
    }

    public long queryTotal(DataModelBO bo) {
        PageHelper.startPage(0, 0);
        return new PageInfo<>(dataModelDao.queryList(bo)).getTotal();
    }

    @WriteTransactional
    public int save(DataModelBO bo) {
        if (Tool.isBlank(bo.getName())) throw new CommonException("数据模型名称不能为空！");

        if (Tool.isNotBlank(bo.getFolderName())) {
            List<DataFolderBO> dataFolderList = dataFolderDao.queryByName(bo.getFolderName());
            if (!dataFolderList.isEmpty()) {
                bo.setFolderId(dataFolderList.getFirst().getId());
            }
        }

        if (Tool.isBlank(bo.getCode())) {
            do {
                bo.setCode("dm_" + UUID.getUUID());
            } while (dataModelDao.existsCode(bo) > 0);
        }

        String type = bo.getType();
        bo.setType(Constants.DataModel.TYPE_LOCAL_TABLE.equals(type) ? Constants.DataModel.TYPE_TABLE : type);

        int result = 0;
        boolean isNew = false;
        if (Tool.isNotBlank(bo.getId())) {
            bo.setUpdateTime(System.currentTimeMillis());
            result += dataModelDao.update(bo);
        } else {
            bo.setId(UUID.getUUID());
            bo.setUpdateTime(System.currentTimeMillis());
            bo.setCreateTime(System.currentTimeMillis());
            result += dataModelDao.insert(bo);
            isNew = true;
        }

        // 保存字段信息
        bo.setType(type);
        result += dataFieldService.save(bo);

        bo.getIndexList().stream()
                .filter(index -> index.getIndexFieldId().startsWith("temp_"))
                .forEach(index -> bo.getFieldList().stream()
                        .filter(field -> ("temp_" + field.getFieldName()).equals(index.getIndexFieldId()))
                        .findFirst()
                        .ifPresent(field -> index.setIndexFieldId(field.getId())));
        // 保存索引信息
        result += dataIndexService.save(bo.getId(), bo.getIndexList(), bo.getCreateId());

        if (isNew) {
            // 创建默认表格
            dataTableService.createDefault(bo);

            // 创建默认表单
            dataFormService.createDefault(bo);
        }

        // 更新触发器
        codelessTrigger.updateTrigger(bo);

        CacheUtil.remove(CacheKey.DataModel.PREFIX + bo.getId());
        CacheUtil.remove(CacheKey.DataModel.PREFIX + bo.getCode());
        MessageUtil.publish(Topic.DATA_FIELD_CHANGE);

        return result;
    }

    @WriteTransactional
    public void saveList(List<DataModelBO> list) {
        if (list == null) return;

        list.forEach(item -> {
            if (dataModelDao.queryBy(item.getId()) != null) {
                item.setUpdateTime(System.currentTimeMillis());
                dataModelDao.update(item);

                CacheUtil.remove(CacheKey.DataModel.PREFIX + item.getId());
                CacheUtil.remove(CacheKey.DataModel.PREFIX + item.getCode());
            } else {
                item.setUpdateTime(System.currentTimeMillis());
                item.setCreateTime(System.currentTimeMillis());
                dataModelDao.insert(item);
            }

            // 更新触发器
            codelessTrigger.updateTrigger(item);
        });
    }

    @WriteTransactional
    public int delete(DataModelBO bo) {
        if (Tool.isBlank(bo.getId())) return 0;

        DataModelBO dataModel = dataModelDao.queryBy(bo.getId());
        if (dataModel == null) return 0;

        if (Constants.YES.equals(dataModel.getBuiltin())) throw new CommonException("内置数据模型，不允许删除！");

        // 判断是否被引用，无引用时才可以删除
        if (dataModelDao.queryUseCnt(bo.getId()) > 0) throw new CommonException("数据模型已被引用，不允许删除！");

        // 删除表单
        dataFormService.deleteByModelId(dataModel.getId());

        // 删除表格
        dataTableService.deleteByModelId(dataModel.getId());

        // 删除字段
        dataFieldService.deleteByModelId(dataModel.getCode(), dataModel.getId());

        // 删除索引
        dataIndexService.deleteByModelId(dataModel.getId());

        // 更新触发器
        dataModel.setBeforeInsertId(null);
        dataModel.setBeforeUpdateId(null);
        dataModel.setBeforeDeleteId(null);
        dataModel.setAfterInsertId(null);
        dataModel.setAfterUpdateId(null);
        dataModel.setAfterDeleteId(null);
        codelessTrigger.updateTrigger(dataModel);

        CacheUtil.remove(CacheKey.DataModel.PREFIX + dataModel.getId());
        CacheUtil.remove(CacheKey.DataModel.PREFIX + dataModel.getCode());
        return dataModelDao.delete(dataModel.getId());
    }

    @WriteTransactional
    public int updateFolder(DataModelBO bo) {
        bo.setUpdateTime(System.currentTimeMillis());
        int result = dataModelDao.updateFolder(bo);

        bo = dataModelDao.queryBy(bo.getId());
        CacheUtil.remove(CacheKey.DataModel.PREFIX + bo.getId());
        CacheUtil.remove(CacheKey.DataModel.PREFIX + bo.getCode());
        return result;
    }

    @WriteTransactional
    public void createTable(List<DataModelBO> list) {
        list.forEach(dataModel -> {
            if (!Constants.DataModel.TYPE_TABLE.equals(dataModel.getType())) return;

            DataFieldBO fieldQO = new DataFieldBO();
            fieldQO.setModelId(dataModel.getId());
            dataModel.setFieldList(dataFieldService.queryList(fieldQO));

            DataFieldRelevanceBO fieldRelevanceQO = new DataFieldRelevanceBO();
            fieldRelevanceQO.setFieldIds(dataModel.getFieldList().stream()
                    .map(DataFieldBO::getId)
                    .toList()
                    .toArray(new String[0]));
            if (fieldRelevanceQO.getFieldIds() != null && fieldRelevanceQO.getFieldIds().length > 0) {
                List<DataFieldRelevanceBO> relevanceList = dataFieldRelevanceService.queryList(fieldRelevanceQO);
                if (!relevanceList.isEmpty()) {
                    dataModel.getFieldList().forEach(field -> field.setRelevanceList(
                            relevanceList.stream()
                                    .filter(relevance -> field.getId().equals(relevance.getFieldId()))
                                    .toList()));
                }
            }

            dataFieldService.save(dataModel);
        });
    }
}
